using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace hao_Common.Dapper.Query
{
    public class ExpressionQueryBuilder<T> : IQueryBuilder<T>
    {
        private readonly StringBuilder _query = new StringBuilder();
        private readonly StringBuilder _select = new StringBuilder();
        private readonly StringBuilder _orderBy = new StringBuilder();
        private readonly StringBuilder _groupBy = new StringBuilder();
        private readonly Dictionary<string, object> _param = new Dictionary<string, object>();
        private readonly Dictionary<string, int> _nameIndexs = new Dictionary<string, int>();
        
        private bool _hasQuery = false;
        private string _tableName;
        private readonly string _safeLeft = "`";
        private readonly string _safeRight = "`";
        private readonly string _preParamName = "@";
        
        // 操作符映射
        private readonly Dictionary<ExpressionType, string> _operators = new Dictionary<ExpressionType, string>
        {
            { ExpressionType.Equal, "=" },
            { ExpressionType.NotEqual, "!=" },
            { ExpressionType.GreaterThan, ">" },
            { ExpressionType.GreaterThanOrEqual, ">=" },
            { ExpressionType.LessThan, "<" },
            { ExpressionType.LessThanOrEqual, "<=" },
            { ExpressionType.AndAlso, " and " },
            { ExpressionType.OrElse, " or " }
        };
        
        public Dictionary<string, object> Param => _param;
        
        public ExpressionQueryBuilder()
        {
            _tableName = typeof(T).Name;
        }
        
        public ExpressionQueryBuilder(string tableName)
        {
            _tableName = tableName;
        }
        
        public IQueryBuilder<T> Where(Expression<Func<T, bool>> exp)
        {
            if (exp == null) return this;
            
            if (_hasQuery)
            {
                _query.Append(" and ");
            }
            else
            {
                _hasQuery = true;
            }
            
            _query.Append("(");
            ParseExpression(exp.Body);
            _query.Append(")");
            
            return this;
        }
        
        public IQueryBuilder<T> OrWhere(Expression<Func<T, bool>> exp)
        {
            if (exp == null) return this;
            
            if (!_hasQuery)
            {
                throw new InvalidOperationException("OrWhere必须跟在已有查询条件后面");
            }
            
            _query.Append(" or (");
            ParseExpression(exp.Body);
            _query.Append(")");
            
            return this;
        }
        
        public IQueryBuilder<T> OrderBy<TResult>(Expression<Func<T, TResult>> exp)
        {
            if (_orderBy.Length > 0) _orderBy.Append(", ");
            _orderBy.Append(GetMemberName(exp));
            return this;
        }
        
        public IQueryBuilder<T> OrderByDescending<TResult>(Expression<Func<T, TResult>> exp)
        {
            if (_orderBy.Length > 0) _orderBy.Append(", ");
            _orderBy.Append($"{GetMemberName(exp)} DESC");
            return this;
        }
        
        public IQueryBuilder<T> GroupBy<TResult>(Expression<Func<T, TResult>> exp)
        {
            if (_groupBy.Length > 0) _groupBy.Append(", ");
            _groupBy.Append(GetMemberName(exp));
            return this;
        }
        
        public IQueryBuilder<T> Select<TResult>(Expression<Func<T, TResult>> exp)
        {
            _select.Clear();
            _select.Append(GetMemberName(exp));
            return this;
        }
        
        public IQueryBuilder<T> SelectCount()
        {
            _select.Clear();
            _select.Append("COUNT(*)");
            return this;
        }
        
        public IQueryBuilder<T> Limit(int count)
        {
            // 实现分页逻辑
            return this;
        }
        
        public IQueryBuilder<T> Skip(int count)
        {
            // 实现跳过逻辑
            return this;
        }
        
        public IQueryBuilder<T> Update<TResult>(Expression<Func<T, TResult>> newExp)
        {
            // 实现更新逻辑
            return this;
        }
        
        public string GetQuery()
        {
            var sql = new StringBuilder();
            
            // SELECT部分
            sql.Append("SELECT ");
            if (_select.Length > 0)
            {
                sql.Append(_select);
            }
            else
            {
                sql.Append("*");
            }
            
            // FROM部分
            sql.Append($" FROM {_safeLeft}{_tableName}{_safeRight}");
            
            // WHERE部分
            if (_hasQuery)
            {
                sql.Append(" WHERE ");
                sql.Append(_query);
            }
            
            // GROUP BY部分
            if (_groupBy.Length > 0)
            {
                sql.Append(" GROUP BY ");
                sql.Append(_groupBy);
            }
            
            // ORDER BY部分
            if (_orderBy.Length > 0)
            {
                sql.Append(" ORDER BY ");
                sql.Append(_orderBy);
            }
            
            return sql.ToString();
        }
        
        public string GetCountQuery()
        {
            var sql = new StringBuilder();
            sql.Append($"SELECT COUNT(*) FROM {_safeLeft}{_tableName}{_safeRight}");
            
            if (_hasQuery)
            {
                sql.Append(" WHERE ");
                sql.Append(_query);
            }
            
            return sql.ToString();
        }
        
        // 核心表达式解析方法
        private void ParseExpression(Expression exp)
        {
            switch (exp.NodeType)
            {
                case ExpressionType.AndAlso:
                case ExpressionType.OrElse:
                    ParseBinaryExpression(exp as BinaryExpression);
                    break;
                case ExpressionType.Call:
                    ParseMethodCall(exp as MethodCallExpression);
                    break;
                default:
                    ParseComparison(exp as BinaryExpression);
                    break;
            }
        }
        
        private void ParseBinaryExpression(BinaryExpression exp)
        {
            var isOr = exp.NodeType == ExpressionType.OrElse;
            
            if (isOr) _query.Append("(");
            ParseExpression(exp.Left);
            _query.Append(_operators[exp.NodeType]);
            ParseExpression(exp.Right);
            if (isOr) _query.Append(")");
        }
        
        private void ParseComparison(BinaryExpression exp)
        {
            // 获取属性名
            string propertyName = GetMemberName(exp.Left);
            
            // 获取值
            object value = GetExpressionValue(exp.Right);
            
            // 处理NULL值
            if (value == null)
            {
                var nullOp = exp.NodeType == ExpressionType.Equal ? "IS NULL" : "IS NOT NULL";
                _query.Append($"{_safeLeft}{propertyName}{_safeRight} {nullOp}");
                return;
            }
            
            // 添加参数
            string paramName = AddParameter(propertyName, value);
            string op = _operators[exp.NodeType];
            
            _query.Append($"{_safeLeft}{propertyName}{_safeRight} {op} {_preParamName}{paramName}");
        }
        
        private void ParseMethodCall(MethodCallExpression exp)
        {
            switch (exp.Method.Name)
            {
                case "Contains":
                    ParseContains(exp);
                    break;
                case "StartsWith":
                    ParseStartsWith(exp);
                    break;
                case "EndsWith":
                    ParseEndsWith(exp);
                    break;
                default:
                    throw new NotSupportedException($"不支持的方法: {exp.Method.Name}");
            }
        }
        
        private void ParseContains(MethodCallExpression exp)
        {
            if (exp.Object != null && exp.Object.Type == typeof(string))
            {
                // 字符串Contains -> LIKE
                string propertyName = GetMemberName(exp.Object);
                object value = GetExpressionValue(exp.Arguments[0]);
                string paramName = AddParameter(propertyName, $"%{EscapeLikeValue(value.ToString())}%");
                
                _query.Append($"{_safeLeft}{propertyName}{_safeRight} LIKE {_preParamName}{paramName} ESCAPE '\\'");
            }
            else
            {
                // 集合Contains -> IN
                string propertyName = GetMemberName(exp.Arguments[0]);
                object collection = GetExpressionValue(exp.Object);
                
                if (collection is System.Collections.IEnumerable enumerable)
                {
                    var values = enumerable.Cast<object>().ToArray();
                    if (values.Length == 0)
                    {
                        _query.Append("1=0"); // 空集合
                        return;
                    }
                    
                    if (values.Length == 1)
                    {
                        string paramName = AddParameter(propertyName, values[0]);
                        _query.Append($"{_safeLeft}{propertyName}{_safeRight} = {_preParamName}{paramName}");
                        return;
                    }
                    
                    _query.Append($"{_safeLeft}{propertyName}{_safeRight} IN (");
                    for (int i = 0; i < values.Length; i++)
                    {
                        string paramName = AddParameter($"{propertyName}_{i}", values[i]);
                        _query.Append($"{_preParamName}{paramName}");
                        if (i < values.Length - 1) _query.Append(", ");
                    }
                    _query.Append(")");
                }
            }
        }
        
        private void ParseStartsWith(MethodCallExpression exp)
        {
            string propertyName = GetMemberName(exp.Object);
            object value = GetExpressionValue(exp.Arguments[0]);
            string paramName = AddParameter(propertyName, $"{EscapeLikeValue(value.ToString())}%");
            
            _query.Append($"{_safeLeft}{propertyName}{_safeRight} LIKE {_preParamName}{paramName} ESCAPE '\\'");
        }
        
        private void ParseEndsWith(MethodCallExpression exp)
        {
            string propertyName = GetMemberName(exp.Object);
            object value = GetExpressionValue(exp.Arguments[0]);
            string paramName = AddParameter(propertyName, $"%{EscapeLikeValue(value.ToString())}");
            
            _query.Append($"{_safeLeft}{propertyName}{_safeRight} LIKE {_preParamName}{paramName} ESCAPE '\\'");
        }
        
        private string GetMemberName(Expression exp)
        {
            if (exp is MemberExpression member)
            {
                return member.Member.Name;
            }
            
            if (exp is UnaryExpression unary && unary.Operand is MemberExpression unaryMember)
            {
                return unaryMember.Member.Name;
            }
            
            throw new ArgumentException($"无法获取成员名称: {exp}");
        }
        
        private object GetExpressionValue(Expression exp)
        {
            if (exp is ConstantExpression constant)
            {
                return constant.Value;
            }
            
            // 编译并执行表达式
            return Expression.Lambda(exp).Compile().DynamicInvoke();
        }
        
        private string AddParameter(string name, object value)
        {
            string paramName = name;
            
            if (_param.ContainsKey(paramName))
            {
                if (!_nameIndexs.ContainsKey(name))
                    _nameIndexs[name] = 1;
                
                _nameIndexs[name]++;
                paramName = $"{name}{_nameIndexs[name]}";
            }
            
            _param.Add(paramName, value);
            return paramName;
        }
        
        private string EscapeLikeValue(string value)
        {
            return value.Replace("'", "''").Replace("[", "\\[") 
                       .Replace("]", "\\]").Replace("%", "\\%")
                       .Replace("_", "\\_");
        }
    }
}